package fr.umlv.model;
import java.awt.Desktop;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.nio.ByteBuffer;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.logging.Level;

import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.ProgressMonitor;
import javax.swing.ProgressMonitorInputStream;
import javax.swing.SwingUtilities;
import javax.swing.event.EventListenerList;

import kiyut.swing.statusbar.MyProgressBar;
import kiyut.swing.statusbar.MyProgressInputStream;
import kiyut.swing.statusbar.TransferDialog;

import org.alfresco.cmis.client.AlfrescoDocument;
import org.apache.chemistry.opencmis.client.api.CmisObject;
import org.apache.chemistry.opencmis.client.api.Document;
import org.apache.chemistry.opencmis.client.api.Folder;
import org.apache.chemistry.opencmis.client.api.ItemIterable;
import org.apache.chemistry.opencmis.client.api.ObjectId;
import org.apache.chemistry.opencmis.client.api.ObjectType;
import org.apache.chemistry.opencmis.client.api.OperationContext;
import org.apache.chemistry.opencmis.client.api.Property;
import org.apache.chemistry.opencmis.client.api.QueryResult;
import org.apache.chemistry.opencmis.client.api.Session;
import org.apache.chemistry.opencmis.client.api.Tree;
import org.apache.chemistry.opencmis.client.runtime.OperationContextImpl;
import org.apache.chemistry.opencmis.commons.PropertyIds;
import org.apache.chemistry.opencmis.commons.data.ContentStream;
import org.apache.chemistry.opencmis.commons.data.RepositoryCapabilities;
import org.apache.chemistry.opencmis.commons.data.RepositoryInfo;
import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
import org.apache.chemistry.opencmis.commons.enums.CapabilityChanges;
import org.apache.chemistry.opencmis.commons.enums.CapabilityQuery;
import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships;
import org.apache.chemistry.opencmis.commons.enums.VersioningState;
import org.apache.chemistry.opencmis.commons.exceptions.CmisBaseException;
import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl;
import org.jdesktop.swingx.JXErrorPane;
import org.jdesktop.swingx.error.ErrorInfo;

import fr.umlv.authentication.SessionHandler;
import fr.umlv.cmis.listview.CmisObjectListViewModel;
import fr.umlv.metadata.MetadataWizard;
import fr.umlv.utils.ExceptionUtils;

// TODO: Auto-generated Javadoc
/**
 * The Class ClientModel.
 */
public class ClientModel {

	// object details must not be older than 60 seconds
	/** The Constant OLD. */
	private static final long OLD = 60 * 1000;

	/** The client session. */
	private static ClientSession clientSession;

	/** The current folder. */
	private Folder currentFolder = null;
	
	/** The current children. */
	private List<CmisObject> currentChildren = Collections.emptyList();
	
	/** The current object. */
	private CmisObject currentObject = null;

	/** The listener list. */
	private EventListenerList listenerList = new EventListenerList();

	/** The perspective_restored_ok. */
	public static boolean perspective_restored_ok = false;
	
	/** The bundle. */
	private static ResourceBundle bundle = ResourceBundle.getBundle("kiyut.ekspos.BrowserWindow", SessionHandler.getLocale());
	
	/** The my progress bar. */
	private MyProgressBar myProgressBar = new MyProgressBar();

	/**
	 * Instantiates a new client model.
	 */
	public ClientModel() {
	}

	/**
	 * Adds the folder listener.
	 *
	 * @param listener the listener
	 */
	public void addFolderListener(FolderListener listener) {
		listenerList.add(FolderListener.class, listener);
	}

	/**
	 * Removes the folder listener.
	 *
	 * @param listener the listener
	 */
	public void removeFolderListener(FolderListener listener) {
		listenerList.remove(FolderListener.class, listener);
	}

	/**
	 * Adds the object listener.
	 *
	 * @param listener the listener
	 */
	public void addObjectListener(ObjectListener listener) {
		listenerList.add(ObjectListener.class, listener);
	}

	/**
	 * Removes the object listener.
	 *
	 * @param listener the listener
	 */
	public void removeObjectListener(ObjectListener listener) {
		listenerList.remove(ObjectListener.class, listener);
	}

	/**
	 * Sets the client session.
	 *
	 * @param clientSession the new client session
	 */
	@SuppressWarnings("static-access")
	public synchronized void setClientSession(ClientSession clientSession) {
		this.clientSession = clientSession;
	}

	/**
	 * Gets the client session.
	 *
	 * @return the client session
	 */
	public synchronized ClientSession getClientSession() {
		return clientSession;
	}

	/**
	 * Gets the repository info.
	 *
	 * @return the repository info
	 * @throws Exception the exception
	 */
	public synchronized RepositoryInfo getRepositoryInfo() throws Exception {
		Session session = clientSession.getSession();
		return session.getRepositoryInfo();
	}

	/**
	 * Gets the repository name.
	 *
	 * @return the repository name
	 */
	public synchronized String getRepositoryName() {
		try {
			return getRepositoryInfo().getName();
		} catch (Exception e) {
			return "?";
		}
	}

	/**
	 * Supports query.
	 *
	 * @return true, if successful
	 */
	public synchronized boolean supportsQuery() {
		try {
			RepositoryCapabilities cap = getRepositoryInfo().getCapabilities();
			if (cap == null) {
				return true;
			}

			return (cap.getQueryCapability() != null) && (cap.getQueryCapability() != CapabilityQuery.NONE);
		} catch (Exception e) {
			return false;
		}
	}

	/**
	 * Supports change log.
	 *
	 * @return true, if successful
	 */
	public synchronized boolean supportsChangeLog() {
		try {
			RepositoryCapabilities cap = getRepositoryInfo().getCapabilities();
			if (cap == null) {
				return true;
			}

			return (cap.getChangesCapability() != null) && (cap.getChangesCapability() != CapabilityChanges.NONE);
		} catch (Exception e) {
			return false;
		}
	}

	/**
	 * Load folder.
	 *
	 * @param folderId the folder id
	 * @param byPath the by path
	 * @throws Exception the exception
	 */
	public synchronized void loadFolder(String folderId, boolean byPath) throws Exception {
		try {
			Session session = clientSession.getSession();
			CmisObject folderObject = null;

			if (byPath) {
				folderObject = session.getObjectByPath(folderId);
			} else {
				folderObject = session.getObject(session.createObjectId(folderId));
			}

			if (!(folderObject instanceof Folder)) {
				throw new Exception("Not a folder!");
			}

			List<CmisObject> children = new ArrayList<CmisObject>();
			ItemIterable<CmisObject> iter = ((Folder) folderObject).getChildren(clientSession
					.getFolderOperationContext());
			for (CmisObject child : iter) {
				children.add(child);
			}

			setCurrentFolder((Folder) folderObject, children);
		} catch (Exception ex) {
			setCurrentFolder(null, new ArrayList<CmisObject>(0));
			throw ex;
		}
	}

	/**
	 * Reload folder.
	 *
	 * @throws Exception the exception
	 */
	public synchronized void reloadFolder() throws Exception {
		loadFolder(currentFolder.getId(), false);
	}

	/**
	 * Load object.
	 *
	 * @param objectId the object id
	 * @throws Exception the exception
	 */
	public synchronized void loadObject(String objectId) throws Exception {
		try {
			Session session = clientSession.getSession();
			ObjectId myOBjectId = session.createObjectId(objectId);
			OperationContext myOperationContext = clientSession.getObjectOperationContext();
			CmisObject object = session.getObject(myOBjectId,myOperationContext);
			object.refreshIfOld(OLD);

			setCurrentObject(object);
		} catch (CmisBaseException cre) {
			ExceptionUtils.createCmisJXBaseException(cre, null);
		} catch (Exception ex) {
			setCurrentObject(null);
			throw ex;
		}
	}

	/**
	 * Reload object.
	 *
	 * @throws Exception the exception
	 */
	public synchronized void reloadObject() throws Exception {
		if (currentObject == null) {
			return;
		}

		try {
			Session session = clientSession.getSession();
			CmisObject object = session.getObject(currentObject, clientSession.getObjectOperationContext());
			object.refresh();

			setCurrentObject(object);
		} catch (Exception ex) {
			setCurrentObject(null);
			throw ex;
		}
	}

	/**
	 * Query.
	 *
	 * @param q the q
	 * @param searchAllVersions the search all versions
	 * @param maxHits the max hits
	 * @return the item iterable
	 * @throws Exception the exception
	 */
	public synchronized ItemIterable<QueryResult> query(String q, boolean searchAllVersions, int maxHits)
	throws Exception {
		OperationContext queryContext = new OperationContextImpl(null, false, false, false, IncludeRelationships.NONE,
				null, false, null, false, maxHits);

		Session session = clientSession.getSession();
		return session.query(q, searchAllVersions, queryContext);
	}

	/**
	 * Gets the type descendants.
	 *
	 * @return the type descendants
	 * @throws Exception the exception
	 */
	public synchronized List<Tree<ObjectType>> getTypeDescendants() throws Exception {
		Session session = clientSession.getSession();
		return session.getTypeDescendants(null, -1, true);
	}

	/**
	 * Creates the content stream.
	 *
	 * @param filename the filename
	 * @return the content stream
	 * @throws Exception the exception
	 */
	public ContentStream createContentStream(String filename) throws Exception {
		ContentStream content = null;
		if ((filename != null) && (filename.length() > 0)) {
			File file = new File(filename);
			InputStream stream = new FileInputStream(file);

			content = clientSession.getSession().getObjectFactory()
			.createContentStream(file.getName(), file.length(), MIMETypes.getMIMEType(file), stream);
		}
		return content;
	}
	
    /**
     * Creates the documents with progress bar and wizard.
     *
     * @param parentComponent the parent component
     * @param cmisObjListVModel the cmis obj list v model
     * @param type the type
     * @param filesList the files list
     * @param folder the folder
     * @param versioningState the versioning state
     * @throws Exception the exception
     */
    public synchronized void createDocumentsWithProgressBarAndWizard(final JComponent parentComponent, 
    		final CmisObjectListViewModel cmisObjListVModel,
    		final String type, final List<File> filesList, final Folder folder, final VersioningState versioningState)
    throws Exception {
    	final TransferDialog dialog = new TransferDialog(myProgressBar, bundle.getString("message.send.title"));
       new Thread() {
    	
			@Override
			public void run() {
				ArrayList<Folder> folderCreated = new ArrayList<Folder>();
				try {
					ArrayList<File> files = new ArrayList<File>();
					HashMap<File, Folder> filesToTransfer = new HashMap<File, Folder>();
					for(int i=0; i<filesList.size(); i++) {
						File f = (File)filesList.get(i);
						if (f.isDirectory()) {
							Folder newFolder = ClientModel.createFolder(folder.getPath()+"/", f.getName());
							folderCreated.add(newFolder);
							for(File file:f.listFiles()) {
								if (file.isFile()) {
									filesToTransfer.put(file, newFolder);
									files.add(file);
								}
							}
						} else if (f.isFile()) {
							filesToTransfer.put(f, folder);
							files.add(f);
						}
					}

					Map<String, Map<String,Object>> extensions = new HashMap<String, Map<String,Object>>();
					MetadataWizard.createWizard(files, extensions);	
			    	
					boolean yesToAll = false;
					boolean noToAll = false;
        			for(Map.Entry<File, Folder> fileEntry:filesToTransfer.entrySet()) {
        				File f = fileEntry.getKey();
        				final Map<String, Object> properties = extensions.get(fileEntry.getKey().getName());
	        			if (properties==null) {
	        				// Rollback
	        				for(Folder fo:folderCreated) {
	        					fo.deleteTree(true, null, true);
	        				}
	        				return;
	        			}
        				
	        			dialog.startTransfer(f.getName(), bundle.getString("message.send.text"));
	        			
						properties.put(PropertyIds.NAME, fileEntry.getKey().getName());
						String type2 = type;
						if (properties.containsKey(PropertyIds.OBJECT_TYPE_ID)) {
							type2 = type2+","+properties.get(PropertyIds.OBJECT_TYPE_ID);
						}
						properties.put(PropertyIds.OBJECT_TYPE_ID, type2);

						if ((f.getName() != null) && (f.length() > 0)) {
							final InputStream stream = new FileInputStream(f);
							myProgressBar.setNewOperation(stream, (int)f.length(), files.size());
							ContentStream content = clientSession.getSession().getObjectFactory()
							.createContentStream(f.getName(), f.length(), MIMETypes.getMIMEType(f), /*pmis*/myProgressBar.getMyProgressInputStream());

							CmisObject cmisObj = null;
							try {
								cmisObj = clientSession.getSession().getObjectByPath(folder.getPath()+"/"+f.getName());
							} catch (CmisObjectNotFoundException cae) {
									clientSession.getSession()
									.createDocument(properties, fileEntry.getValue(), content, versioningState, null, null, null);
							}
							
							if (cmisObj != null) {
								Object[] options = {bundle.getString("buttonYes.text"),
										bundle.getString("buttonYesAll.text"),
										bundle.getString("buttonNo.text"),
										bundle.getString("buttonNoAll.text"),
										bundle.getString("buttonCancel.text")};
								CmisObject oldObj = null;
								try {
									if (!yesToAll) {
										if (noToAll)
											continue;
										Object[] args = {f.getName()};
										int n = JOptionPane.showOptionDialog(myProgressBar, 
												MessageFormat.format(bundle.getString("create.version.text"), args),
												bundle.getString("create.version.title"),
												JOptionPane.YES_NO_CANCEL_OPTION, 
												JOptionPane.QUESTION_MESSAGE,
											    null,
											    options,
											    options[0]);
										if ((n == 0)||(n==1)) {
											ObjectId oldObjId = ((Document)cmisObj).checkOut();
											oldObj = clientSession.getSession().getObject(oldObjId);
			
											ContentStream contentStream = new ContentStreamImpl(oldObj.getName(), null, oldObj.getPropertyValue("cmis:contentStreamMimeType").toString(), myProgressBar.getMyProgressInputStream());
											
											properties.put(PropertyIds.NAME, contentStream.getFileName());
											properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document");
			
											((Document)oldObj).checkIn(false, properties, contentStream, null);
											if (n == 1) {
												yesToAll = true;
											}
										} else if (n == 2) {
											stream.close();
											continue;
										} else if (n == 3) {
											noToAll = true;
											stream.close();
											continue;
										} else  {
											throw new InterruptedIOException(bundle.getString("exception.cancelledByUser.text"));
										}
									} else {
										ObjectId oldObjId = ((Document)cmisObj).checkOut();
										oldObj = clientSession.getSession().getObject(oldObjId);
		
										ContentStream contentStream = new ContentStreamImpl(oldObj.getName(), null, oldObj.getPropertyValue("cmis:contentStreamMimeType").toString(), myProgressBar.getMyProgressInputStream());
										
										properties.put(PropertyIds.NAME, contentStream.getFileName());
										properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document");
		
										((Document)oldObj).checkIn(false, properties, contentStream, null);
									}
								} catch(InterruptedIOException ioe) {
									ExceptionUtils.createJXErrorIOException(ioe, parentComponent);
									((Document)oldObj).cancelCheckOut();
								} catch (CmisBaseException e) {
									ExceptionUtils.createCmisJXBaseException(e, parentComponent);
									((Document)oldObj).cancelCheckOut();
								}
							}
							stream.close();
						}
					}
				} catch(CmisBaseException e) {
					ExceptionUtils.createCmisJXBaseException(e, parentComponent);
				} catch(Exception ex) { 
					ExceptionUtils.createJXErrorException(ex, parentComponent);
				} finally {
					SwingUtilities.invokeLater(new Runnable() {
						@Override
						public void run() {
							dialog.endTransfer();
							cmisObjListVModel.refresh();
							try {
								myProgressBar.close();
							} catch (IOException e) {
								System.err.println("Error when closing the progressBar!");
							}
						}
					});
				}
			}
		}.start();
	}

	/**
	 * Creates the documents with progress bar.
	 *
	 * @param parentComponent the parent component
	 * @param cmisObjListVModel the cmis obj list v model
	 * @param type the type
	 * @param filesList the files list
	 * @param folder the folder
	 * @param versioningState the versioning state
	 * @throws Exception the exception
	 */
	public synchronized void createDocumentsWithProgressBar(final JComponent parentComponent, 
			final CmisObjectListViewModel cmisObjListVModel,
			final String type, final List<File> filesList, final Folder folder, final VersioningState versioningState)
	throws Exception {
		final TransferDialog dialog = new TransferDialog(myProgressBar, bundle.getString("message.send.title"));
		new Thread() {
			@Override
			public void run() {
				try {
					ArrayList<File> files = new ArrayList<File>();
					HashMap<File, Folder> filesToTransfer = new HashMap<File, Folder>();
					for(int i=0; i<filesList.size(); i++) {
						File f = (File)filesList.get(i);
						if (f.isDirectory()) {
							Folder newFolder = ClientModel.createFolder(folder.getPath()+"/", f.getName());
							for(File file:f.listFiles()) {
								if (file.isFile()) {
									filesToTransfer.put(file, newFolder);
									files.add(file);
								}
							}
						} else if (f.isFile()) {
							filesToTransfer.put(f, folder);
							files.add(f);
						}
					}
					boolean yesToAll = false;
					boolean noToAll = false;
					for(Map.Entry<File, Folder> fileEntry:filesToTransfer.entrySet()) {
						File f = fileEntry.getKey();
						dialog.startTransfer(f.getName(), bundle.getString("message.send.text"));
						
						final Map<String, Object> properties = new HashMap<String, Object>();
						properties.put(PropertyIds.OBJECT_TYPE_ID, type);
						properties.put(PropertyIds.NAME, fileEntry.getKey().getName());

						if ((f.getName() != null) && (f.length() > 0)) {

							final InputStream stream = new FileInputStream(f);
							myProgressBar.setNewOperation(stream, (int)f.length(), files.size());
							ContentStream content = clientSession.getSession().getObjectFactory()
							.createContentStream(f.getName(), f.length(), MIMETypes.getMIMEType(f), /*pmis*/myProgressBar.getMyProgressInputStream());
							CmisObject oldObj = null;
							CmisObject cmisObj = null;
							try {
								cmisObj = clientSession.getSession().getObjectByPath(folder.getPath()+"/"+f.getName());
							} catch (CmisObjectNotFoundException cae) {
									clientSession.getSession()
									.createDocument(properties, fileEntry.getValue(), content, versioningState, null, null, null);
							}
							
							if (cmisObj != null) {
								Object[] options = {bundle.getString("buttonYes.text"),
										bundle.getString("buttonYesAll.text"),
										bundle.getString("buttonNo.text"),
										bundle.getString("buttonNoAll.text"),
										bundle.getString("buttonCancel.text")};
								try {
									if (!yesToAll) {
										if (noToAll)
											continue;
										Object[] args = {f.getName()};
										int n = JOptionPane.showOptionDialog(myProgressBar, 
												MessageFormat.format(bundle.getString("create.version.text"), args),
												bundle.getString("create.version.title"),
												JOptionPane.YES_NO_CANCEL_OPTION, 
												JOptionPane.QUESTION_MESSAGE,
												null,
												options,
												options[0]);
										if ((n == 0)||(n==1)) {
											ObjectId oldObjId = ((Document)cmisObj).checkOut();
											oldObj = clientSession.getSession().getObject(oldObjId);

											ContentStream contentStream = new ContentStreamImpl(oldObj.getName(), null, oldObj.getPropertyValue("cmis:contentStreamMimeType").toString(), myProgressBar.getMyProgressInputStream());

											properties.put(PropertyIds.NAME, contentStream.getFileName());
											properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document");

											((Document)oldObj).checkIn(false, properties, contentStream, null);
											if (n == 1) {
												yesToAll = true;
											}
										} else if (n == 2) {
											stream.close();
											continue;
										} else if (n == 3) {
											noToAll = true;
											stream.close();
											continue;
										} else  {
											throw new InterruptedIOException(bundle.getString("exception.cancelledByUser.text"));
										}
									} else {
										ObjectId oldObjId = ((Document)cmisObj).checkOut();
										oldObj = clientSession.getSession().getObject(oldObjId);

										ContentStream contentStream = new ContentStreamImpl(oldObj.getName(), null, oldObj.getPropertyValue("cmis:contentStreamMimeType").toString(), myProgressBar.getMyProgressInputStream());

										properties.put(PropertyIds.NAME, contentStream.getFileName());
										properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document");

										((Document)oldObj).checkIn(false, properties, contentStream, null);
									}
								} catch(InterruptedIOException ioe) {
									ExceptionUtils.createJXErrorIOException(ioe, parentComponent);
									((Document)oldObj).cancelCheckOut();
								} catch (CmisBaseException e) {
									ExceptionUtils.createCmisJXBaseException(e, parentComponent);
									((Document)oldObj).cancelCheckOut();
								}
							}
							stream.close();
						}
					}
				} catch(CmisBaseException e) {
					ExceptionUtils.createCmisJXBaseException(e, parentComponent);
				} catch(Exception ex) { 
					ExceptionUtils.createJXErrorException(ex, parentComponent);
				} finally {
					SwingUtilities.invokeLater(new Runnable() {
						@Override
						public void run() {
							dialog.endTransfer();
							cmisObjListVModel.refresh();
							try {
								myProgressBar.close();
							} catch (IOException e) {
							}
						}
					});
				}
			}
		}.start();
	}

	/**
	 * Creates the folder.
	 *
	 * @param path the path
	 * @param folderName the folder name
	 * @return the folder
	 */
	public static Folder createFolder(String path, String folderName) {
		Session session = clientSession.getSession();

		if(!path.startsWith("/") || !path.endsWith("/"))
			throw new IllegalArgumentException("path have to be formated as : / or /<path1>/.../");

		Map<String, Object> properties = new HashMap<String, Object>();
		properties.put(PropertyIds.NAME, folderName);
		properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder");

		Folder folder = (Folder) session.getObjectByPath(path);
		session.createFolder(properties, folder);
		return (Folder) session.getObjectByPath(path+folderName);
	}

	/**
	 * Gets the createable types.
	 *
	 * @param rootTypeId the root type id
	 * @return the createable types
	 */
	public synchronized List<ObjectType> getCreateableTypes(String rootTypeId) {
		List<ObjectType> result = new ArrayList<ObjectType>();

		List<Tree<ObjectType>> types = clientSession.getSession().getTypeDescendants(rootTypeId, -1, false);
		addType(types, result);

		ObjectType rootType = clientSession.getSession().getTypeDefinition(rootTypeId);
		boolean isCreatable = (rootType.isCreatable() == null ? true : rootType.isCreatable().booleanValue());
		if (isCreatable) {
			result.add(rootType);
		}

		Collections.sort(result, new Comparator<ObjectType>() {
			public int compare(ObjectType ot1, ObjectType ot2) {
				return ot1.getDisplayName().compareTo(ot2.getDisplayName());
			}
		});

		return result;
	}

	/**
	 * Adds the type.
	 *
	 * @param types the types
	 * @param resultList the result list
	 */
	private void addType(List<Tree<ObjectType>> types, List<ObjectType> resultList) {
		for (Tree<ObjectType> tt : types) {
			if (tt.getItem() != null) {
				boolean isCreatable = (tt.getItem().isCreatable() == null ? true : tt.getItem().isCreatable()
						.booleanValue());

				if (isCreatable) {
					resultList.add(tt.getItem());
				}

				addType(tt.getChildren(), resultList);
			}
		}
	}

	/**
	 * Gets the current folder.
	 *
	 * @return the current folder
	 */
	public synchronized Folder getCurrentFolder() {
		return currentFolder;
	}

	/**
	 * Gets the current children.
	 *
	 * @return the current children
	 */
	public synchronized List<CmisObject> getCurrentChildren() {
		return currentChildren;
	}

	/**
	 * Gets the from current children.
	 *
	 * @param id the id
	 * @return the from current children
	 */
	public synchronized CmisObject getFromCurrentChildren(String id) {
		if ((currentChildren == null) || (currentChildren.isEmpty())) {
			return null;
		}

		for (CmisObject o : currentChildren) {
			if (o.getId().equals(id)) {
				return o;
			}
		}

		return null;
	}

	/**
	 * Sets the current folder.
	 *
	 * @param folder the folder
	 * @param children the children
	 */
	private synchronized void setCurrentFolder(Folder folder, List<CmisObject> children) {
		currentFolder = folder;
		currentChildren = children;

		for (FolderListener fl : listenerList.getListeners(FolderListener.class)) {
			fl.folderLoaded(new ClientModelEvent(this));
		}
	}

	/**
	 * Gets the current object.
	 *
	 * @return the current object
	 */
	public synchronized CmisObject getCurrentObject() {
		return currentObject;
	}

	/**
	 * Sets the current object.
	 *
	 * @param object the new current object
	 */
	private synchronized void setCurrentObject(CmisObject object) {
		currentObject = object;

		for (ObjectListener ol : listenerList.getListeners(ObjectListener.class)) {
			ol.objectLoaded(new ClientModelEvent(this));
		}
	}


	/**
	 * Gets the stream cmis object.
	 *
	 * @param file the file
	 * @return the stream cmis object
	 * @throws IOException Signals that an I/O exception has occurred.
	 */
	public static ByteBuffer getStreamCmisObject(CmisObject file) throws IOException {

		Document doc_to_download = (Document) file;
		ContentStream stream = doc_to_download.getContentStream();

		InputStream inputStream = stream.getStream();

		ByteArrayOutputStream out = new ByteArrayOutputStream(1024);

		byte[] tmp = new byte[1024];

		int r;
		while ((r = inputStream.read(tmp)) > 0) {
			out.write(tmp,0,r);
		}

		return ByteBuffer.wrap(out.toByteArray());

	}

	/**
	 * Creates the stream cmis object.
	 *
	 * @param pathCMIS the path cmis
	 * @param filenameCMIS the filename cmis
	 * @param contentType the content type
	 * @param data the data
	 * @return the cmis object
	 */
	public static CmisObject createStreamCmisObject(String pathCMIS, String filenameCMIS, String contentType, ByteBuffer data) {
		Session session = clientSession.getSession();

		if(!pathCMIS.startsWith("/") || !pathCMIS.endsWith("/"))
			throw new IllegalArgumentException("pathCMIS have to formated : /<path1>/<path2>/");

		if(contentType.isEmpty() || filenameCMIS.isEmpty()) {
			throw new IllegalArgumentException("filename and contentType must be fill");
		}

		InputStream is = new ByteArrayInputStream(data.array());
		ContentStream contentStream = new ContentStreamImpl(filenameCMIS, null, contentType, is);

		Map<String, Object> properties = new HashMap<String, Object>();
		properties.put(PropertyIds.NAME, contentStream.getFileName());
		properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document");

		Folder folder = (Folder) session.getObjectByPath(pathCMIS);
		session.createDocument(properties, folder, contentStream, VersioningState.NONE);
		return session.getObjectByPath(pathCMIS+filenameCMIS);
	}

	
	/**
	 * Creates the stream cmis object.
	 *
	 * @param target the target
	 * @param filenameCMIS the filename cmis
	 * @param contentType the content type
	 * @param data the data
	 * @param source the source
	 * @return the cmis object
	 */
	public static CmisObject createStreamCmisObject( Folder target, String filenameCMIS, String contentType, ByteBuffer data, CmisObject source) {

		Session session = clientSession.getSession();
		String pathCMIS;

		if("/".equals(target.getPath())) {
			pathCMIS =  target.getPath();
		}else {
			pathCMIS =  target.getPath()+"/";
		}
		
		InputStream is = new ByteArrayInputStream(data.array());
		ContentStream contentStream = new ContentStreamImpl(filenameCMIS, null, contentType, is);

		Map<String, Object> properties = new HashMap<String, Object>();
		properties.put(PropertyIds.NAME, ((Document) source).getContentStream().getFileName());

		AlfrescoDocument doc = (AlfrescoDocument)source;
		StringBuilder sb = new StringBuilder();
		sb.append("cmis:document");
		for (ObjectType prop : doc.getAspects()) {
			sb.append(","+prop.getId());
		}
		
		for (Property<?> prop : source.getProperties()) {

			if (prop.isMultiValued()) {

			}{

				if (prop.getValue() != null ) {
					properties.put(prop.getId(), prop.getValue());
				} else {
				}
			}
		}
		properties.put(PropertyIds.OBJECT_TYPE_ID, sb.toString());
		session.createDocument(properties, target, contentStream, VersioningState.MAJOR);
		return session.getObjectByPath(pathCMIS+filenameCMIS);
	}


	/**
	 * Update stream cmis object.
	 *
	 * @param file the file
	 * @param data the data
	 */
	public static void updateStreamCmisObject(CmisObject file, ByteBuffer data) {
		InputStream is = new ByteArrayInputStream(data.array());
		ContentStream contentStream = new ContentStreamImpl(((Document)file).getPaths().get(0), null, file.getPropertyValue("cmis:contentStreamMimeType").toString(), is);

		((Document)file).setContentStream(contentStream, true, true);
	}

	/**
	 * Creates the stream versioned cmis object.
	 *
	 * @param file the file
	 * @param data the data
	 * @return the cmis object
	 */
	public static CmisObject createStreamVersionedCmisObject(CmisObject file, ByteBuffer data) {
		Session session = clientSession.getSession();

		ObjectId oldObjId = ((Document)file).checkOut();
		CmisObject oldObj = session.getObject(oldObjId);

		InputStream is = new ByteArrayInputStream(data.array());
		ContentStream contentStream = new ContentStreamImpl(oldObj.getName(), null, oldObj.getPropertyValue("cmis:contentStreamMimeType").toString(), is);

		Map<String, Object> properties = new HashMap<String, Object>();
		properties.put(PropertyIds.NAME, contentStream.getFileName());
		properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document");

		ObjectId newObjId = ((Document)oldObj).checkIn(false, properties, contentStream, null);

		return session.getObject(newObjId);
	}

	/**
	 * Download cmis object.
	 *
	 * @param cmisFile the cmis file
	 * @param dst the dst
	 * @throws IOException Signals that an I/O exception has occurred.
	 */
	public static void downloadCmisObject(CmisObject cmisFile, File dst) throws IOException {
		FileOutputStream fos = null;
		try {
			fos = new FileOutputStream(dst);

			ByteBuffer bb = getStreamCmisObject(cmisFile);
			fos.write(bb.array());

		} catch (IOException e) {
			ErrorInfo info = new ErrorInfo(e.getClass().getName(), e.getMessage(), null, "category", e, Level.ALL, null);
			JXErrorPane.showDialog(null, info); 
		} finally {
			try {
				fos.close();
			} catch (IOException e) {
				ErrorInfo info = new ErrorInfo(e.getClass().getName(), e.getMessage(), null, "category", e, Level.ALL, null);
				JXErrorPane.showDialog(null, info); 
			}
		}
	}

	/**
	 * Download cmis object with progress bar.
	 *
	 * @param parentComponent the parent component
	 * @param cmisObjToFile the cmis obj to file
	 */
	public static void downloadCmisObjectWithProgressBar(final JComponent parentComponent, final HashMap<CmisObject, File> cmisObjToFile) {
		new Thread() {
			@Override
			public void run() {
				parentComponent.setEnabled(false);
				for (Map.Entry<CmisObject, File> elmt : cmisObjToFile.entrySet()) {
					final CmisObject cmisFile = elmt.getKey();
					final File dst = elmt.getValue();
					 if (BaseTypeId.CMIS_FOLDER.equals(cmisFile.getBaseTypeId())) {
						 parentComponent.setEnabled(true);
						 return;
					 }
					FileOutputStream fos = null;
					Document doc_to_download = (Document) cmisFile;
					ContentStream stream = doc_to_download.getContentStream();

					final InputStream inputStream = stream.getStream();
					final ProgressMonitorInputStream pmis = new ProgressMonitorInputStream(parentComponent,
							bundle.getString("message.receive.text") + dst.getName(),
							inputStream);
					ProgressMonitor pm = pmis.getProgressMonitor();
					byte[] tmp = new byte[1024];

					try {
						fos = new FileOutputStream(dst);
						int r;
						long total = doc_to_download.getContentStreamLength();
						pm.setMaximum((int) total);

						while ((r = pmis.read(tmp)) > 0) {
							fos.write(tmp, 0, r);
						}
						pm.close();
					} catch (InterruptedIOException iie) {
						ExceptionUtils.createJXErrorInterruptedIOException(iie, null);
				    } catch (IOException e) {
				    	ExceptionUtils.createJXErrorIOException(e, null);
					} finally {
						try {
							fos.close();
							pmis.close();
						} catch (IOException e) {
							ExceptionUtils.createJXErrorIOException(e, null);
						}
						SwingUtilities.invokeLater(new Runnable() {
							@Override
							public void run() {
								parentComponent.setEnabled(true);
							}
						});
					}
				}
			}
		}.start();
	}

	/**
	 * Open cmis object.
	 *
	 * @param file the file
	 * @throws IOException Signals that an I/O exception has occurred.
	 */
	public static void openCmisObject(CmisObject file) throws IOException {
		FileOutputStream fos = null;

		String fileSelected = file.getName();
		String ext;
		if (fileSelected.lastIndexOf(".") > 0) {
			// On récupère l'extension du fichier
			ext = fileSelected.substring(fileSelected.lastIndexOf("."));
		} else {
			// sinon c'est que l'utilisateur n'a pas entré d'extension
			// donc on met en .txt
			ext = ".txt";
		}
		try {
			// Get the path of new file
			File tmpFile = File.createTempFile(file.getName()+"_", ext);

			fos = new FileOutputStream(tmpFile);

			ByteBuffer bb = getStreamCmisObject(file);
			fos.write(bb.array());

			// On vérifie que la classe Desktop soit bien
			// supportée :
			if (Desktop.isDesktopSupported()) {
				// On récupère l'instance du desktop :
				Desktop desktop = Desktop.getDesktop();

				// On vérifie que la fonction open est bien
				// supportée :
				if (desktop.isSupported(Desktop.Action.OPEN)) {

					// Et on lance l'application associé au
					// fichier pour l'ouvrir :
					desktop.open(tmpFile);
				}
			}

		} catch (IOException e) {
			ExceptionUtils.createJXErrorIOException(e, null);
		} finally {
			try {
				fos.close();
			} catch (IOException e) {
				ExceptionUtils.createJXErrorIOException(e, null);
			}
		}
	}

	/**
	 * Download multiple cmis object with progress bar.
	 *
	 * @param parentComponent the parent component
	 * @param cmisFiles the cmis files
	 * @param dstFolder the dst folder
	 * @param isFile the is file
	 */
	public void downloadMultipleCmisObjectWithProgressBar(final JComponent parentComponent, final List<CmisObject> cmisFiles, final File dstFolder, final boolean isFile) {
		final TransferDialog dialog = new TransferDialog(myProgressBar, bundle.getString("message.receive.title"));
		
    	final ArrayList<CmisObject> files = new ArrayList<CmisObject>();
    	for(CmisObject cmisFile:cmisFiles) {
    		if (BaseTypeId.CMIS_DOCUMENT.equals(cmisFile.getBaseTypeId())) {
    			files.add(cmisFile);
    		} else {
    			new File(dstFolder.getAbsolutePath()+ File.separatorChar + cmisFile.getName()).mkdir();
    		}
    	}
    	
		new Thread() {
			@Override
			public void run() {
				FileOutputStream fos = null;
				try {
					parentComponent.setEnabled(false);
					boolean yesToAll = false;
					boolean noToAll = false;
					for (CmisObject file : files) {
						Document doc_to_download = (Document) file;
						ContentStream stream = doc_to_download.getContentStream();

						final InputStream inputStream = stream.getStream();
						dialog.startTransfer(file.getName(), bundle.getString("message.receive.text"));
						myProgressBar.setNewOperation(inputStream, (int)doc_to_download.getContentStreamLength(), files.size());
						byte[] tmp = new byte[1024];
						File newFile = null;
						if (isFile) {
							newFile = dstFolder;
						} else {
							newFile = new File(dstFolder+File.separator+file.getName());
						}
						Object[] options = {bundle.getString("buttonYes.text"),
								bundle.getString("buttonYesAll.text"),
								bundle.getString("buttonNo.text"),
								bundle.getString("buttonNoAll.text"),
								bundle.getString("buttonCancel.text")};
						
						if (!yesToAll&&newFile.exists()) {
							if (noToAll)
								continue;
							Object[] args = {file.getName()};
							int n = JOptionPane.showOptionDialog(myProgressBar, 
									MessageFormat.format(bundle.getString("exception.alreadyexistslocal.text"), args),
									bundle.getString("exception.alreadyexists.title"),
									JOptionPane.YES_NO_CANCEL_OPTION, 
									JOptionPane.QUESTION_MESSAGE,
								    null,
								    options,
								    options[0]);
							if (n == 0) {
								
							} else if (n == 1) {
								yesToAll = true;
							} else if (n == 2) {
								continue;
							} else if (n == 3) {
								noToAll = true;
								continue;
							} else {
								throw new InterruptedIOException(bundle.getString("exception.cancelledByUser.text"));
							}
						}
						fos = new FileOutputStream(newFile);
						MyProgressInputStream myProgressIs = myProgressBar.getMyProgressInputStream();
						int r;
						while ((r = myProgressIs.read(tmp)) > 0) {
							fos.write(tmp, 0, r);
						}
					}
				} catch (InterruptedIOException iie) {
					ExceptionUtils.createJXErrorInterruptedIOException(iie, null);
				} catch (IOException e) {
					ExceptionUtils.createJXErrorIOException(e, null);
				} finally {
					if (fos!= null)
						try {
							fos.close();
						} catch (IOException e1) {}
					SwingUtilities.invokeLater(new Runnable() {
						@Override
						public void run() {
							dialog.endTransfer();
							parentComponent.setEnabled(true);
							try {
								myProgressBar.close();
							} catch (IOException e) {
							}
						}
					});
				}
			}
		}.start();
	}
	
	/**
	 * Copy cmis object.
	 *
	 * @param src the src
	 * @param dst the dst
	 */
	public static void copyCmisObject(CmisObject src, CmisObject dst) {

		Session session = clientSession.getSession();
		Document srcDocN = (Document)src;
		ObjectId dstFolder = session.getObject(dst);
		final String type =src.getProperty(PropertyIds.OBJECT_TYPE_ID).getValuesAsString();

		try {
			ContentStream srcContentStream = srcDocN.getContentStream();

			final Map<String, Object> properties = new HashMap<String, Object>();
			properties.put(PropertyIds.NAME, srcDocN.getName());
			properties.put(PropertyIds.OBJECT_TYPE_ID, type);

			session.createDocument(properties, dstFolder, srcContentStream, VersioningState.MAJOR);
		} catch (CmisBaseException cre) {
			ExceptionUtils.createCmisJXBaseException(cre, null);
		}
	}

}
